#include <sys/types.h>          
#include <sys/socket.h>
#include <sys/un.h>
#include <netinet/in.h>

#include <fcntl.h>
#include <errno.h>
#include <unistd.h>

#include "ipc_api.h"

ipcMessageHandle_s ipcMessage={IPC_APPID_INVALID,-1,IPC_MESSAGE_ORIGIN};
ipcMessageHandle_s *pIPCMessage = &ipcMessage;

static ipcRet_e messageWaitData(int	fd,int timeoutInMiliSecond)
{
	ipcRet_e ret = ipcSuccess;
	int retVal = 0;
	struct timeval tm;
	fd_set rdfdset;

	FD_ZERO(& rdfdset);
	FD_SET(fd, &rdfdset);
	
	tm.tv_sec = timeoutInMiliSecond/MILLISECOND_IN_SECOND;
	tm.tv_usec = (timeoutInMiliSecond % MILLISECOND_IN_SECOND) * MICROSECOND_IN_MILLISECOND;
	
	retVal = select(fd+1, &rdfdset,NULL,NULL,&tm);
	if(retVal < 0)
	{
		ipcLogError("messageWaitData failed errno %d\n",errno);
		ret = ipcErrorSyscall;
	}
	else if(retVal == 1)
	{
		retVal = FD_ISSET(fd,&rdfdset);
		if(1 != retVal)
			ret = ipcErrorSyscall;
	}
	else if(retVal == 0)
	{
		ipcLogDebug("messageWaitData timeout\n");
		ret = ipcErrorTimeout;
	}

	return ret;
}

ipcRet_e messageSend(int	fd,ipcMessage_s *pMessage)
{
	ipcRet_e ret = ipcSuccess;
	int retVal = 0;

	ipcLogCheckNullPointReturnValue(pMessage,ipcErrorInvalidPara);
									
	retVal = write(fd,pMessage,sizeof(ipcMessage_s));
	if(retVal < 0)
	{
		if (errno == EPIPE)
		{
			ipcLogError("ipcMessageSend failed EPIPE, dst app may dead\n");
			ret = ipcErrorSyscall;
		}
		else
		{
			ipcLogError("ipcMessageSend failed errno %d\n",errno);
			ret = ipcErrorSyscall;
		}
		
	}
	else if(retVal != sizeof(ipcMessage_s))
	{
		ipcLogError("ipcMessageSend failed expect %d,actual %d \n",sizeof(ipcMessage_s),retVal);
		ret = ipcErrorMisc;
	}
	else
	{
		ipcLogDebug("ipcMessageSend fd %d src %d dst %d type %d msglen %d\n",
					 fd,pMessage->appIdSrc,pMessage->appIdDst,pMessage->msgType,retVal);
	}

	return ret;
}

ipcRet_e messageRecv(int	fd,ipcMessage_s *pMessage,int timeoutInMillisecond)
{
	ipcRet_e ret = ipcSuccess;
	int retVal = 0;

	ipcLogCheckNullPointReturnValue(pMessage,ipcErrorInvalidPara);
	if(fd < 0)
	{
		ipcLogError("ipcMessageRecv failed fd 0x%x\n",fd);
		return ipcErrorInvalidPara;
	}

	if(0 != timeoutInMillisecond)
	{
		ret = messageWaitData(fd, timeoutInMillisecond);
		ipcLogCheckRetThenReturn(ret);
	}

	retVal = read(fd,pMessage,sizeof(ipcMessage_s));

	if(retVal < 0)
	{
		if (errno == ECONNRESET)
		{
			ipcLogError("ipcMessageRecv failed ECONNRESET\n");
			ret = ipcErrorDisconnect;
		}
		else
		{
			ipcLogError("ipcMessageRecv failed errno %d\n",errno);
			ret = ipcErrorSyscall;
		}
		
	}
	else if(retVal == 0)
	{
		ipcLogError("ipcMessageRecv failed to read data \n");
		ret = ipcErrorDisconnect;
	}
	else
	{
		ipcLogDebug("ipcMessageRecv fd %d src %d dst %d type %d msglen %d\n",
					 fd,pMessage->appIdSrc,pMessage->appIdDst,pMessage->msgType,retVal);
	}

	return ret;
}


ipcRet_e ipcUnixServerSocketInit(const char * path ,int backlog,int *pListenFd)
{
	ipcRet_e ret = ipcSuccess;
	int sockfd = 0;
	int retVal = 0;
	struct sockaddr_un my_addr;
	
	ipcLogCheckNullPointReturnValue(pListenFd,ret);

	unlink(path);

	sockfd = socket(AF_UNIX,SOCK_STREAM,0);
	if(-1 == sockfd)
	{
		ipcLogDebug("func %s socket failed",__FUNCTION__);
		return ipcErrorSyscall;
	}

	PUT_ZERO_2_MEMORY(&my_addr, sizeof(my_addr));
	my_addr.sun_family = AF_UNIX;
	strncpy(my_addr.sun_path, path,sizeof(my_addr.sun_path) - 1);
	
	retVal = bind(sockfd, (const struct sockaddr *)&my_addr,sizeof(my_addr));
	if(-1 == retVal)
	{
		close(sockfd);
		ipcLogDebug("func %s bind failed",__FUNCTION__);
		return ipcErrorSyscall;
	}

	retVal = listen(sockfd, backlog);
	if(-1 == retVal)
	{
		close(sockfd);
		ipcLogDebug("func %s listen failed",__FUNCTION__);
		return ipcErrorSyscall;
	}
	
	*pListenFd = sockfd;
	//ipcLogDebug("func %s listen fd %d\n",__FUNCTION__,*pListenFd);

	return ret;
}

ipcRet_e ipcUnixServerSocketDeinit(const char * path ,int *pListenFd)
{
	ipcRet_e ret = ipcSuccess;
	ipcLogCheckNullPointReturnValue(pListenFd,ipcErrorInvalidPara);

	unlink(path);
	
	if(*pListenFd < 0)
	{
		ipcLogError("func %s failed pListenFd is %d  \n",__FUNCTION__,*pListenFd);
		return ipcErrorInvalidPara;
	}

	close(*pListenFd);
	*pListenFd = IPC_INVALID_FD;
	
	return ret;
}

ipcRet_e ipcUnixClientInit(char * path,void **msgHandle)
{
	ipcRet_e ret = ipcSuccess;
	int fileControl = 0;
	ipcMessageHandle_s *pHandle = pIPCMessage;
	struct sockaddr_un serverAddr;
	
	ipcLogCheckNullPointReturnValue(msgHandle,ipcErrorInvalidPara);

	pHandle->appId = IPC_APPID_INVALID;
	pHandle->messageFd = IPC_INVALID_FD;
	pHandle->msgStatus = IPC_MESSAGE_ORIGIN;
	
	pHandle->messageFd = socket(AF_UNIX,SOCK_STREAM,0);
	if(pHandle->messageFd < 0)
	{
		ipcLogError("func %s socket failed \n",__FUNCTION__);
		return ipcErrorSyscall;
	}

	fileControl = fcntl(pHandle->messageFd,F_SETFD,FD_CLOEXEC);
	if(fileControl != 0)
	{
		close(pHandle->messageFd);
		ipcLogError("func %s fcntl failed \n",__FUNCTION__);
		return ipcErrorSyscall;
	}

	memset(&serverAddr,0,sizeof(serverAddr));
	serverAddr.sun_family = AF_UNIX;
	strncpy(serverAddr.sun_path,path,sizeof(serverAddr.sun_path)-1);

	fileControl = connect(pHandle->messageFd,(struct sockaddr *)&serverAddr,sizeof(serverAddr));
	if(fileControl != 0)
	{
		close(pHandle->messageFd);
		ipcLogError("func %s connect failed fd %d path %s\n",__FUNCTION__,pHandle->messageFd,serverAddr.sun_path);
		return ipcErrorSyscall;
	}

	*msgHandle = pHandle;
	pIPCLibContext->messageHandle =  pHandle;
	
	return ret;
}

ipcRet_e ipcUnixClientDeinit(void **msgHandle)
{
	ipcRet_e ret = ipcSuccess;
	ipcMessageHandle_s *pHandle = NULL;
	
	ipcLogCheckNullPointReturnValue(msgHandle,ipcErrorInvalidPara);

	pHandle = (ipcMessageHandle_s*)*msgHandle;
	ipcLogCheckNullPointReturnValue(pHandle,ipcErrorInvalidPara);

	if(pHandle->messageFd < 0)
	{
		ipcLogError("func %s pHandle->messageFd %d \n",__FUNCTION__,pHandle->messageFd);
		return ipcErrorInvalidPara;
	}
	
	close(pHandle->messageFd);
	*msgHandle = NULL;
	pIPCLibContext->messageHandle = NULL;
	
	return ret;
}

ipcRet_e ipcMessageSend(void *messageHandle,ipcMessage_s *pMessage)
{
	ipcMessageHandle_s *pHandle = NULL;
	ipcRet_e ret = ipcSuccess;
	
	ipcLogCheckNullPointReturnValue(messageHandle,ipcErrorInvalidPara);

	pHandle = 	(ipcMessageHandle_s *)messageHandle;

	//ret = ipcMessageClientCanSend(messageHandle);
	//ipcLogCheckRetThenReturn(ret);
	
	ret = messageSend(pHandle->messageFd,pMessage);
	if(ipcSuccess == ret)
	{
		pHandle->msgStatus = IPC_MESSAGE_SEND_DONE;
	}

	return ret;
}

ipcRet_e ipcMessageRecv(void *messageHandle,ipcMessage_s *pMessage,int timeoutInMillisecond)
{
	ipcMessageHandle_s *pHandle = NULL;	
	ipcRet_e ret = ipcSuccess;
	ipcLogCheckNullPointReturnValue(messageHandle,ipcErrorInvalidPara);

	pHandle = (ipcMessageHandle_s *)messageHandle;

	ret = messageRecv(pHandle->messageFd, pMessage, timeoutInMillisecond);
	if(ipcSuccess == ret)
	{
		pHandle->msgStatus = IPC_MESSAGE_RECV_DONE;
	}

	return ret;
}

ipcRet_e ipcMessageSendWithoutData(void*messageHandle,APPID_e src,APPID_e dst,ipcMessageType_e type)
{
	ipcRet_e 	ret = ipcSuccess;
	ipcMessage_s message;

	PUT_ZERO_2_MEMORY(&message, sizeof(message));
	
	message.appIdDst = dst;
	message.appIdSrc = src;
	message.msgType = type;

	ret = ipcMessageSend(messageHandle, &message);

	return ret;
}

ipcRet_e ipcMessageClientCanSend(void *messageHandle)
{
	ipcMessageHandle_s *pHandle = (ipcMessageHandle_s *)messageHandle;
	
	if(IPC_MESSAGE_SEND_DONE == pHandle->msgStatus)
	{
		ipcLogError("func %s only support send-recv-send-recv mode. ");
		ipcLogError("one message send but we still do not have a response\n");
		return ipcErrorMisc;
	}
	else{
		return ipcSuccess;
	}

}

